/*************************************************
 * ThinkJot V2
 * Author: Jeswin P. (jeswin@process64.com)
 * Website: http://www.process64.com/thinkjot/
 * This code is distributed under the terms of the
 * Apache License, Version 2.0.
 * **********************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;

using ThinkJot.Core.Utils;
using ThinkJot.Core.Configuration;

namespace ThinkJot.Core.Blogs.Providers.Xml
{
    public class XmlLogger : Logger
    {
        const int lockTimeOut = 100000; //100 seconds

        private class BlogLogLocks
        {
            object logLock = new object();
            public object LogLock
            {
                get { return logLock; }
            }
            object activityLogLock = new object();
            public object ActivityLogLock
            {
                get { return activityLogLock; }
            }
        }

        #region static members
        static object systemLogLock = new object();

        static bool blogLogLocksInitialized = false;
        static object blogLogLocksInitLock = new object();
        static Dictionary<string, BlogLogLocks> blogLogLocksList = new Dictionary<string, BlogLogLocks>();

        static void InitBlogLogLockList()
        {
            lock (blogLogLocksInitLock)
            {
                if (blogLogLocksInitialized)
                    return;
                Dictionary<string, BlogConfig> blogConfigs = SystemConfiguration.GetConfig().Blogs.GetAll();
                foreach (BlogConfig blogConfig in blogConfigs.Values)
                {
                    blogLogLocksList[blogConfig.Name] = new BlogLogLocks();
                }
                blogLogLocksInitialized = true;
            }
        }
        #endregion
        
        //The methods call into this....
        private void WriteToLogWithLock(string logFilePath, Guid eventId, string what, object lockObj)
        {
            lock(lockObj)
                WriteToLogImpl(eventId, what, logFilePath);
        }

        private string ReadFileWithLock(string logFilePath, object lockObj)
        {
            string result;
            if (!File.Exists(logFilePath)) return null;

            lock (lockObj)
            {
                using (StreamReader sr = new StreamReader(logFilePath))
                {
                    result = sr.ReadToEnd();
                }
            }
            return result;
        }

        //The method which actually does all the logging
        private void WriteToLogImpl(Guid eventId, string what, string logFilePath)
        {
            if (string.IsNullOrEmpty(what)) return;
            try
            {
                using (StreamWriter sw = File.AppendText(logFilePath))
                {
                    sw.WriteLine("ThinkJotLog_Event:" + eventId.ToString() + "; " +
                        "ThinkJotLog_Time:" + DateTime.Now.ToUniversalTime().ToString());
                    if (what.Contains("ThinkJotLog_Event:"))
                        what.Replace("ThinkJotLog_Event:", "'ThinkJotLog_Event:'"); //Crude escape. It will suffice.

                    sw.WriteLine(what);
                }
            }
            catch
            {
                //There is nothing we can do here, really!
            }
        }
        
        public override void WriteToLog(Guid eventId, string what, string blogName)
        {
            if (!blogLogLocksInitialized)
                InitBlogLogLockList();
            string logFilePath = GetLogFilename(DateTime.UtcNow, blogName);
            WriteToLogWithLock(logFilePath, eventId, what, blogLogLocksList[blogName].LogLock);
        }

        public override void WriteToSystemLog(Guid eventId, string what)
        {
            string logFilePath = GetSystemLogFilename(DateTime.UtcNow);
            WriteToLogWithLock(logFilePath, eventId, what, systemLogLock);
        }
        
        //Calls WriteToLog, so no locking required
        public override void LogException(Exception ex, string blogName)
        {
            if (!blogLogLocksInitialized)
                InitBlogLogLockList();
            Guid eventId = Guid.NewGuid();
            if (ex != null)
            {
                WriteToLog(eventId, ex.Message, blogName);
                WriteToLog(eventId, "__StackTrace__" + ex.StackTrace, blogName);
                if (ex.InnerException != null) WriteToLog(eventId, "__InnerException__" + ex.InnerException.Message, blogName);
            }
        }

        //Calls WriteToSystemLog, so no locking required.
        public override void LogSystemException(Exception ex)
        {
            Guid eventId = Guid.NewGuid();
            if (ex != null)
            {
                WriteToSystemLog(eventId, ex.Message);
                WriteToSystemLog(eventId, "__StackTrace__" + ex.StackTrace);
                if (ex.InnerException != null) WriteToSystemLog(eventId, "__InnerException__" + ex.InnerException.Message);
            }
        }

        public override void WriteToActivityLog(Guid eventId, string what, string blogName)
        {
            if (!blogLogLocksInitialized)
                InitBlogLogLockList();
            string logFilePath = GetActivityLogFilename(DateTime.UtcNow, blogName);
            WriteToLogWithLock(logFilePath, eventId, what, blogLogLocksList[blogName].ActivityLogLock);
        }

        public override string ReadLog(DateTime date, string blogName)
        {
            if (!blogLogLocksInitialized)
                InitBlogLogLockList();
            return ReadFileWithLock(GetLogFilename(date, blogName), blogLogLocksList[blogName].LogLock);
        }

        public override string ReadSystemLog(DateTime date)
        {
            return ReadFileWithLock(GetSystemLogFilename(date), systemLogLock);
        }

        public override string ReadActivityLog(DateTime date, string blogName)
        {
            if (!blogLogLocksInitialized)
                InitBlogLogLockList();
            return ReadFileWithLock(GetActivityLogFilename(date, blogName), blogLogLocksList[blogName].ActivityLogLock);
        }

        private string GetLogFilename(DateTime date, string blogName)
        {
            string dataFolder = Path.Combine(ProviderSettings.GetBlogContentFolder(blogName), "logs");
            string fileName = "log-" + date.ToShortDateString().Replace('/', '-') + ".txt";
            return Path.Combine(dataFolder, fileName);
        }

        private string GetSystemLogFilename(DateTime date)
        {
            string dir = Path.Combine(ProviderSettings.GetDataFolder(), "tj_system_logs");
            string fileName = "systemlog-" + date.ToShortDateString().Replace('/', '-') + ".txt";
            return Path.Combine(dir, fileName);
        }

        private string GetActivityLogFilename(DateTime date, string blogName)
        {
            string dataFolder = Path.Combine(ProviderSettings.GetBlogContentFolder(blogName), "logs");
            string fileName = "activity-" + date.ToShortDateString().Replace('/', '-') + ".txt";
            return Path.Combine(dataFolder, fileName);
        }
    }
}
